package space

import (
	"math"
	"src/protoMsg"
	"src/zeus/common"
	"src/zeus/iserver"
	"src/zeus/linmath"
	"src/zeus/msgdef"

	"src/zeus/logger"
)

const (
	// aoiRange     = 50.0
	aoiTolerance = 1.0
	maxAOICount  = 50
)

type IAOIPacker interface {
	GetID() uint64
	GetType() string
	GetAOIProp() msgdef.IMsg
	GetAOIUpdateProp() *protoMsg.PropBaseSyncList
	GetBaseProps() []byte
}

// IAOISender 断线重连之后打包发送完整AOI信息
type IAOISender interface {
	SendFullAOIs() error
}

// SendFullAOIs 发送完整的AOI信息
func (e *Entity) SendFullAOIs() error {
	msg := &protoMsg.AOIMsg{
		EnterAOIs: make([]*protoMsg.EnterAOI, 0, 100),
		IsEnd:     false,
	}

	if e.GetSpace() == nil {
		return nil
	}
	size := 0
	// iCount := 0
	e.aoiList.Walk(AOI_All, func(data common.Keyer) bool {
		ip, ok := data.(IAOIPacker)
		if !ok {
			e.Error("Get AOIPacker failed")
			return true
		}
		if e.GetID() == ip.GetID() {
			return true
		}
		// //可以忽略的加满最大值，不能忽略的，不受这个限制
		// if e.IsWatcher() {
		// 	iCount++
		// 	if iCount >= maxAOICount {
		// 		return false
		// 	}
		// }
		logger.Debug(logger.LogKey_AOI, "SendFullAOIs===", ip.GetID(), "==== 进入AOI")
		if !ok {
			e.Error("Get AOIPacker failed")
			return false
		}
		propBytes := ip.GetAOIProp()
		m := &protoMsg.EnterAOI{
			Prop: &protoMsg.PropSyncList{
				EntityID:   ip.GetID(),
				EntityType: ip.GetType(),
				Prop:       propBytes.(*protoMsg.PropBaseSyncList),
			},
		}
		if size >= 100 {
			e.PostToClient(msg)
			msg = &protoMsg.AOIMsg{
				EnterAOIs: make([]*protoMsg.EnterAOI, 0, 100),
				IsEnd:     false,
			}
			size = 0
		}
		msg.EnterAOIs = append(msg.EnterAOIs, m)
		size++
		return true
	})
	if size > 0 {
		e.PostToClient(msg)
	}
	return nil
}

// SetWatcher 设置当前entity 为watcher
func (e *Entity) SetWatcher(b bool) {
	e._isWatcher = b
}

// IsWatcher 是否观察者
func (e *Entity) IsWatcher() bool {
	return e._isWatcher
}

func (e *Entity) SetNeedUpdateAOI(isNeed bool) {
	e.needUpdateAOI = isNeed
}

func (e *Entity) IsNeedUpdateAOI() bool {
	return e.needUpdateAOI
}

func (e *Entity) SetAOIReady(isReady bool) {
	e.AOIReady = isReady
}

func (e *Entity) IsAOIReady() bool {
	return e.AOIReady
}

func (e *Entity) determineAOIFlag(pos linmath.Vector3) {

	//如果是初始化的坐标系，忽略
	if e.lastAOIPos.IsInValid() {
		return
	}

	updataDist := 0.1 //aoiRange * 0.01
	// protoVec3 := e.Position.GetValue().(*linmath.Vector3)
	if math.Abs(float64(pos.GetX()-e.lastAOIPos.X)) > updataDist ||
		math.Abs(float64(pos.GetY()-e.lastAOIPos.Y)) > updataDist ||
		math.Abs(float64(pos.GetZ()-e.lastAOIPos.Z)) > updataDist {
		e.SetNeedUpdateAOI(true)
	}
}

func (e *Entity) updatePosCoord(pos linmath.Vector3) {

	if e.needUpdateAOI {
		s := e.GetSpace()
		if s != nil {
			s.UpdateCoord(e.GetRealPtr().(iserver.ICoordEntity))
		}

		e.lastAOIPos = pos
		e.SetNeedUpdateAOI(false)
	}
}

// AddExtWatchEntity 增加额外关注对象
func (e *Entity) AddExtWatchEntity(o iserver.ICoordEntity, isUpdate2BeWatcher bool) {
	if e.extWatchList == nil {
		e.extWatchList = make(map[uint64]*extWatchEntity)
	}

	if _, ok := e.extWatchList[o.GetID()]; ok {
		return
	}

	inMyAOI := false
	if e.GetSpace() != nil {
		e.GetSpace().TravsalAOI(e, func(n iserver.ICoordEntity) bool {
			// 已经在AOI范围内
			if n.GetID() == o.GetID() {
				inMyAOI = true
				return true
			}
			return false
		})
	}

	// if !inMyAOI {
	// 	e.OnEntityEnterAOI(o)
	// }

	e.extWatchList[o.GetID()] = &extWatchEntity{
		entity:             o,
		isInAOI:            inMyAOI,
		isUpdate2BeWatcher: isUpdate2BeWatcher,
	}

	//这里记录下被观察者
	if bewather, ok := o.(IBeWatcher); ok && e.IsAOIReady() {
		// 这里其实要控制 isupdate参数，这里先这样
		bewather.AddExtWatchEntity(e, isUpdate2BeWatcher)
	}

	if isUpdate2BeWatcher {
		// 不管是否在AOI范围内，都加入到AOI列表中，因为需要同步o的信息
		e.AddAOIEntity(o)
	}
}

// RemoveExtWatchEntity 删除额外关注对象
func (e *Entity) RemoveExtWatchEntity(o iserver.ICoordEntity) {
	if e.extWatchList == nil {
		return
	}

	ext, ok := e.extWatchList[o.GetID()]
	if !ok {
		return
	}

	// inMyAOI := false

	// if e.GetSpace() != nil {
	// 	e.GetSpace().TravsalAOI(e, func(n iserver.ICoordEntity) {
	// 		// 已经在AOI范围内
	// 		if n.GetID() == o.GetID() {
	// 			inMyAOI = true
	// 		}
	// 	})
	// }

	delete(e.extWatchList, o.GetID())
	//这里记录下被观察者
	if bewather, ok := o.(IBeWatcher); ok {
		bewather.RemoveExtWatchEntity(e)
	}
	if ext.isUpdate2BeWatcher && !ext.isInAOI {
		// 要检查是否在AOI范围内，不在AOI范围内的，删除掉，不在获取它的信息，AOI范围内的，仍然保留，否则会出现AOI范围内的实体被销毁的奇怪表现
		e.RemoveAoiEntity(o)
	}

	// if !inMyAOI {
	// 	e.OnEntityLeaveAOI(o)
	// }
}

func (e *Entity) clearExtWatchs() {
	for _, we := range e.extWatchList {
		e.RemoveExtWatchEntity(we.entity)
	}
}

// TravsalExtWatchs 遍历额外观察者列表
func (e *Entity) TravsalExtWatchs(f func(*extWatchEntity)) {
	if len(e.extWatchList) == 0 {
		return
	}

	for _, extWatch := range e.extWatchList {
		f(extWatch)
	}
}

// OnEntityEnterAOI 实体进入AOI范围
func (e *Entity) OnEntityEnterAOI(o iserver.ICoordEntity) {
	if e.GetID() == o.GetID() {
		return
	}
	// 当o在我的额外关注列表中时, 不触发真正的EnterAOI, 只是打个标记
	if extWatch, ok := e.extWatchList[o.GetID()]; ok {
		extWatch.isInAOI = true
		return
	}
	e.AddAOIEntity(o)
}

// OnAfterEntityEnterAOI 实体进入AOI范围以后执行
func (e *Entity) OnAfterEntityEnterAOI(o iserver.ICoordEntity) {
}

// OnEntityLeaveAOI 实体离开AOI范围
func (e *Entity) OnEntityLeaveAOI(o iserver.ICoordEntity) {
	// 当o在我的额外关注列表中时, 不触发真正的LeaveAOI
	if extWatch, ok := e.extWatchList[o.GetID()]; ok {
		extWatch.isInAOI = false
		return
	}
	e.RemoveAoiEntity(o)
}

// AddAOIEntity 添加AOI列表
func (e *Entity) AddAOIEntity(o iserver.ICoordEntity) {

	if e._isWatcher {
		//e.aoies = append(e.aoies, AOIInfo{true, o})
		// 我是玩家，对方对玩家不可见，所以我看不见他
		if o.GetPlayerHide() && e.GetID() != o.GetAOIOwnerUID() {
			e.RemoveAoiEntity(o)
			return
		}
		// if o.GetAOIOwnerUID()
		e.enterList.Push(o)
		// e.leaveList.Remove(o)
	}

	if o.IsBeLinked() {
		linkers := o.GetLinkedEntity()
		for _, l := range linkers {
			if e._isWatcher {
				//e.aoies = append(e.aoies, AOIInfo{true, l})
				e.enterList.Push(l)
			}
			// //关联列表现在还没怎么用，后面根据实际使用情况调整
			// if bewather, ok := o.(IBeWatcher); ok {
			// 	bewather.InsertWatcher(&extWatchEntity{
			// 		entity: l,
			// 	})
			// }
		}
	}
}

// RemoveAoiEntity 删除AOI列表元素
func (e *Entity) RemoveAoiEntity(o iserver.ICoordEntity) {
	if e._isWatcher {
		//e.aoies = append(e.aoies, AOIInfo{false, o})
		if e.aoiList.Exists(o) {
			e.leaveList.Push(o)
		} else {
			e.enterList.Remove(o)
		}
	}

	// 如果Entity处于委托状态, 并且委托的目标离开了AOI范围, 则取消委托
	// TODO: 临时策略
	if e.IsEntrusted() {
		if e.entrustTarget.GetID() == o.GetID() {
			e.UnEntrust()
		}
	}

	if o.IsBeLinked() {
		linkers := o.GetLinkedEntity()
		for _, l := range linkers {
			if e._isWatcher {
				//e.aoies = append(e.aoies, AOIInfo{false, l})
				if e.aoiList.Exists(l) {
					e.leaveList.Push(l)
				} else {
					e.enterList.Remove(l)
				}
			}

			// if l.IsWatcher() {
			// 	//这里记录下被观察者
			// 	if bewather, ok := o.(IBeWatcher); ok {
			// 		bewather.RemoveWatcher(l.GetID())
			// 	}
			// }
		}
	}
}

const AOIMAX = 600

func (e *Entity) updateAOI() {

	if !e.IsAOIReady() {
		return
	}

	if e._isWatcher {
		msg := &protoMsg.AOIMsg{
			IsEnd:     false,
			LeaveAOIs: make([]*protoMsg.LeaveAOI, 0, e.leaveList.Size()),
		}
		size := 0
		//离开视野
		e.leaveList.WalkAndRemove(func(data common.Keyer) bool {
			eo := data.(iserver.ICoordEntity)
			ip := eo.(IAOIPacker)

			m := &protoMsg.LeaveAOI{
				EntityID: ip.GetID(),
			}
			msg.LeaveAOIs = append(msg.LeaveAOIs, m)
			e.aoiList.Remove(eo)
			size++
			return true
		})
		msg.UpdateAOIs = make([]*protoMsg.UpdateAOI, 0, 10)
		//刷新视野
		e.aoiList.Walk(AOI_All, func(en common.Keyer) bool {

			// ie := en.(iserver.ICoordEntity)
			ip, ok := en.(IAOIPacker)
			if !ok {
				e.Error("Get AOIPacker failed")
				return true
			}
			if e.GetID() == ip.GetID() {
				return true
			}
			propBytes := ip.GetAOIUpdateProp()
			if propBytes == nil || len(propBytes.Prop) == 0 {
				return true
			}
			m := &protoMsg.UpdateAOI{
				Prop: &protoMsg.PropSyncList{
					EntityID: ip.GetID(),
					// EntityType: ip.GetType(),
					Prop: propBytes,
				},
			}
			if size >= AOIMAX {
				e.PostToClient(msg)
				msg = &protoMsg.AOIMsg{
					UpdateAOIs: make([]*protoMsg.UpdateAOI, 0, 10),
					IsEnd:      false,
				}
				size = 0
			}
			msg.UpdateAOIs = append(msg.UpdateAOIs, m)
			size += 3
			return true
		})
		msg.EnterAOIs = make([]*protoMsg.EnterAOI, 0, e.enterList.Size())
		re, reok := e.GetRealPtr().(iserver.IAOITriggerEntity)
		//进入视野
		e.enterList.WalkAndRemove(func(data common.Keyer) bool {
			eo := data.(iserver.ICoordEntity)
			ip := eo.(IAOIPacker)

			if !e.aoiList.CanAdd(e.GetID(), eo) {
				return true
			}
			propBytes := ip.GetAOIProp()
			if propBytes == nil {
				e.Error("updateAOI isEnter GetAOIProp failed")
				return true
			}
			m := &protoMsg.EnterAOI{
				Prop: &protoMsg.PropSyncList{
					EntityID:   ip.GetID(),
					EntityType: ip.GetType(),
					Prop:       propBytes.(*protoMsg.PropBaseSyncList),
				},
			}

			if reok {
				e.space.CallDelay(func() {
					re.OnAfterEntityEnterAOI(eo.GetRealPtr().(iserver.ICoordEntity))
				})
			}
			if size >= AOIMAX {
				e.PostToClient(msg)
				msg = &protoMsg.AOIMsg{
					IsEnd:     false,
					EnterAOIs: make([]*protoMsg.EnterAOI, 0, e.enterList.Size()),
				}
				size = 0
			}
			msg.EnterAOIs = append(msg.EnterAOIs, m)
			e.aoiList.Push(eo)
			size += 6
			return true
		})

		if size > 0 {
			msg.IsEnd = true
			e.PostToClient(msg)

			// if logger.CheckKey(logger.LogKey_AOI) && len(msg.EnterAOIs) > 0 {
			// 	logger.Debug(logger.LogKey_AOI, e.GetID(), "更新AOI数据-------------------------------")
			// 	logger.Debug(logger.LogKey_AOI, "EnterAOI数据：")
			// 	for _, v := range msg.EnterAOIs {
			// 		logger.Debug(logger.LogKey_AOI, "ID:", v.Prop.EntityID, " Type:", v.Prop.EntityType)
			// 	}
			// 	// logger.Debug(logger.LogKey_AOI, "UpdateAOI:")
			// 	// for _, v := range msg.UpdateAOIs {
			// 	// 	logger.Debug(logger.LogKey_AOI, "ID:", v.Prop.EntityID, " Type:", v.Prop.EntityType)
			// 	// }
			// 	// logger.Debug(logger.LogKey_AOI, "LeaveAOI:")
			// 	// for _, v := range msg.LeaveAOIs {
			// 	// 	logger.Debug(logger.LogKey_AOI, "ID:", v.EntityID)
			// 	// }
			// }
		}
	}
}

// GetAOILayerIndex 获取AOI的层级
func (e *Entity) GetAOILayerIndex() uint8 {
	return e.aoiIndex
}

// SetAOILayerIndex 设置AOI的层级
func (e *Entity) SetAOILayerIndex(i uint8) {
	e.aoiIndex = i
}

// IsAOITrigger 是否要解发AOITrigger事件
func (e *Entity) IsAOITrigger() bool {
	return true //e.IsWatcher()
}

// SetNeedHide 是否需要在AOI中隐藏
func (e *Entity) SetNeedHide(hide bool) {
	e.needHide = hide
}

// GetNeedHide 是否需要在AOI中隐藏
func (e *Entity) GetNeedHide() bool {
	return e.needHide
}

// SetHide 是否已经在AOI中隐藏
func (e *Entity) SetHide(hide bool) {
	e.hide = hide
}

// GetHide 是否已经在AOI中隐藏
func (e *Entity) GetHide() bool {
	return e.hide
}

// 是否对玩家隐藏
func (e *Entity) GetPlayerHide() bool {
	return e.playerhide
}

// 隐藏别人 针对我(记录在我这边)
func (e *Entity) SetHideOthersToMe(v bool) {
	if e.aoiList.hideOthers == v {
		return
	}
	logger.Debugf(logger.LogKey_LoginFlow, "SetServerSign SetHideOthersToMe %v (oldVal:%v)", v, e.aoiList.hideOthers)

	e.aoiList.SetHideOthersPlayer(v)
	if v {
		//不能看见的话，就把看见的别的玩家都去掉
		e.aoiList.Walk(AOI_All, func(data common.Keyer) bool {
			o := data.(iserver.ICoordEntity)
			aoiowneruid := o.GetAOIOwnerUID()
			if aoiowneruid != 0 &&
				aoiowneruid != e.GetID() {
				e.OnEntityLeaveAOI(data.(iserver.ICoordEntity))
			}
			return true
		})
		e.GetSpace().RefreshShow(e.GetRealPtr().(iserver.ICoordEntity))
	} else {
		e.GetSpace().RefreshShow(e.GetRealPtr().(iserver.ICoordEntity))
	}
}

// 隐藏自己 针对别人(记录在我这边)
func (e *Entity) SetHideMeToOthers(v bool) {
	if e.playerhide == v {
		return
	}
	logger.Debugf(logger.LogKey_LoginFlow, "SetServerSign SetHideMeToOthers %v (oldVal:%v)", v, e.playerhide)
	e.playerhide = v
	// e.needUpdateAOI = true
	// e.GetSpace().RefreshShow(e.GetRealPtr().(iserver.ICoordEntity))
	/*
		需要遍历子对象
	*/
	e.GetSpace().TravsalAOINoCheck(e, func(ie iserver.ICoordEntity) bool {
		if ie.GetAOIOwnerUID() == e.GetID() {
			e.GetSpace().RefreshShow(ie.GetRealPtr().(iserver.ICoordEntity))
		}
		return false
	})
}

// 获取AOI系统中需要知道的主人ID
func (e *Entity) GetAOIOwnerUID() uint64 {
	return e.GetID()
}

// GetKey 获取在maplist中的key
func (e *Entity) GetKey() interface{} {
	return e.GetID()
}

// 在不在对方的AOI内
func (e *Entity) ExistsAOIWeather(o iserver.ICoordEntity) bool {
	return e.aoiList.Exists(o)
}
